//
//  ZZBannerView.swift
//  CzzSwTest
//
//  Created by 陈钟 on 2018/9/20.
//  Copyright © 2018年 陈钟. All rights reserved.
//

import UIKit

@objc(BannerDelegate)
public protocol BannerDelegate : NSObjectProtocol {
    @objc optional func changed(page: Int, banner: ZZBannerView)
    @objc optional func changed(proportion: CGFloat, banner: ZZBannerView)
    @objc optional func bannerClicked(object: Any, banner: ZZBannerView, index: NSInteger)
    @objc optional func createBannerImages(imageView: UIImageView, object:Any)
}

@objc(ZZBannerView)
open class ZZBannerView: UIView, UIScrollViewDelegate {

    public override init(frame: CGRect = CGRect.zero) {
        super.init(frame: frame)
        createView()
    }

    @objc public init(frame: CGRect = CGRect.zero, bannerArr: [Any], block: ((_ page:Int, _ imageView : UIImageView, _ object : Any) -> ())? = nil) {
        super.init(frame: frame)
        createView()
        self.createBannerImagesBlock = block
        self.bannerArr = bannerArr
        self.pageControl.numberOfPages = self.bannerArr?.count ?? 0
        self.pageControl.currentPage = 0
        self.writeBanner()
    }

    /// banner滚动回调
    @objc public var changedBlock : ((_ page:Int,_ imageView : UIView, _ banner:ZZBannerView) -> ())?
    /// banner滚动回调 带比例
    @objc public var changedProportionBlock : ((_ proportion:CGFloat, _ banner:ZZBannerView) -> ())?

    /// banner点击回调
    @objc public var bannerClickedBlock : ((_ object:Any,_ banner:ZZBannerView,_ page:NSInteger) -> ())?

    /// 1、创建banner images调用该方法对图片数据处理
    /// 2、必须在setbannerarr 之前设置该block
    /// 3、只能是bannerarr 的数据对象可以直接 named获取到时可不调用
    @objc public var createBannerImagesBlock : ((_ page:Int, _ imageView : UIImageView, _ object : Any) -> ())?

    @objc public var isAddGestureWhenCreate : ((_ page:Int, _ imageView : UIImageView, _ object : Any) -> Bool)?

    //MARK: - ZZBannerView配置
    
    /// 存贮上一次调用的page如果和上次一样 不在调用
    private var oldPage : Int = -1
    
    /// 存储绘制的banner按钮们
    private var bannerImagesArr: [UIView] = []
    
    /// 代理 必须用weak
    @objc public weak var delegate : BannerDelegate?
    
    /// pageControl 的居中方式
    @objc public var pageControlAlignment : ZZPageControl.PageControlAlignment = .Center {
        didSet{
//            changePageControlAlignment()
            self.pageControl.alignment = self.pageControlAlignment
        }
    }
    
    /// 自动滚动banner的时间间隔 默认2s
    @objc public var bannerRunDuration : TimeInterval = 2

    /// 滚动时动画时长
    @objc public var pageAnimationDuration: TimeInterval = 0.25

    /// 自动滚动banner定时器
    private var autoRunTimer : Timer?
    
    /// 是否自动滚动
    @objc public var isAutoRun : Bool = false{
        didSet{
            if self.isAutoRun {
                runBanner()
            }
        }
    }
    
    /// 两个显示图片间的间隔
    @objc public var itemSpace : CGFloat = 0{
        didSet{
            updateSubImagesFrame()
        }
    }
    
    /// 是否循环滚动banner
    @objc public var isCycleRunBanner : Bool = false{
        didSet{//每次重新绘制BannerImages
            writeBanner()
        }
    }

    /// 展示区域inset
    @objc public var inset: UIEdgeInsets = UIEdgeInsets.zero{
        didSet{
            self.updateSubImagesFrame()
        }
    }

    /// banner scrollview inset
    @objc public var contentInset: UIEdgeInsets = UIEdgeInsets.zero{
        didSet{
            self.updateSubImagesFrame()
        }
    }

    ///
    @objc public var pageOffset: CGPoint = CGPoint.zero{
        didSet{
            self.updateSubImagesFrame()
        }
    }
    
    /// banner数据 当bannerarr 的数据对象可以直接 named获取,可不调用createBannerImagesBlock
    @objc public var bannerArr : [Any]?{//banner数据
        didSet{
            writeBanner()
            self.pageControl.numberOfPages = self.bannerArr?.count ?? 0
            self.pageControl.currentPage = 0
        }
    }
    
    /// 自定义Banner显示窗体 如果UIView 中是自动布局 需要设置bannerview 的高度或者宽度 >= top+bottom 或者 left + right
    @objc public var customViews: [UIView]?{
        didSet{
            self.writeBanner()
            self.pageControl.numberOfPages = self.customViews?.count ?? 0
            self.pageControl.currentPage = 0
        }
    }
    
    //MARK: - views
    @objc public lazy var pageControl : ZZPageControl = {
        let pageview = ZZPageControl.init()
        pageview.height = 20;
        pageview.pageIndicatorTintColor = UIColor.init(white: 1, alpha: 0.5)
        pageview.currentPageIndicatorTintColor = UIColor.init(white: 0, alpha: 0.5)
        pageview.alignment = self.pageControlAlignment
        self.addSubview(pageview)
        return pageview
    }()
    
    @objc public lazy var contentView : UIScrollView = {
        let view = UIScrollView.init()
        view.isPagingEnabled = true
        view.delegate = self
        view.layer.masksToBounds = false
        view.showsVerticalScrollIndicator = false
        view.showsHorizontalScrollIndicator = false
        self.addSubview(view)
        return view
    }()

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        createView()
    }

    override public func layoutSubviews() {
        super.layoutSubviews()
        updateSubImagesFrame()
    }

    deinit {
        self.autoRunTimer?.invalidate()
        self.autoRunTimer = nil
    }
    
    public func currcentItem() -> (UIView?, Any?){
        return self.bannerItem(with: self.pageControl.currentPage)
    }

    public func bannerItem(with page: Int) -> (UIView?, Any?){
        var pageCount: Int = page
        if self.isCycleRunBanner && self.customViews == nil {
            pageCount += 1
        }
        let view = self.bannerImagesArr.objAt(index: pageCount)
        let obj = self.bannerArr?.objAt(index: page)
        return (view, obj)
    }
    
    private func runBanner(){
        if (self.autoRunTimer != nil) {
            self.autoRunTimer?.invalidate()
            self.autoRunTimer = nil
        }
        if !self.isAutoRun{
            return
        }
        if #available(iOS 10.0, *) {
            self.autoRunTimer = Timer.scheduledTimer(withTimeInterval: self.bannerRunDuration, repeats: true, block: { [weak self] timer in
                self?.setContentOffset()
            })
        } else {
            self.autoRunTimer = Timer.scheduledTimer(timeInterval: self.bannerRunDuration, target: self, selector: #selector(self.setContentOffset), userInfo: nil, repeats: true)
        }
        RunLoop.current.add(self.autoRunTimer!, forMode: .default)//添加runloop 用户在操作界面的时候autoRunTimer停止工作
    }


    @objc private func setContentView(offset: CGPoint, animation: Bool, isRunBlock: Bool){
        guard animation else {
            self.contentView.contentOffset = offset
            self.refreshPage(isRunBlock: isRunBlock)
            return
        }
        UIView.animate(withDuration: 0.25, delay: 0, options: .curveLinear, animations: { [weak self] in
            self?.contentView.contentOffset = offset
        }, completion: { [weak self] _ in
            self?.refreshPage(isRunBlock: isRunBlock)
        })
    }

    @objc private func setContentOffset(){
        let offset = self.contentView.contentOffset
        let bounds = self.contentView.frame
        let page = offset.x / bounds.size.width;
        let nextPage = page + 1;
        if self.isCycleRunBanner && self.customViews == nil {
            self.setContentView(offset:  CGPoint.init(x: nextPage * self.contentView.width, y: 0), animation: true, isRunBlock: true)
        }else{
            var count: Int = 0
            if (self.customViews?.count ?? 0) > 0 {
                count = (self.customViews?.count)! - 1
            }else{
                count = (self.bannerArr?.count)! - 1
            }
            if Int(nextPage) > count{
                self.setContentView(offset: .zero, animation: true, isRunBlock: true)
            }else{
                self.setContentView(offset: CGPoint(x: nextPage * self.contentView.width, y: 0), animation: true, isRunBlock: true)
            }
        }
    }
    
    @objc open func createView() {
    }
    
    /// 绘制banner但不进行frame设置
    private func writeBanner() {
        for view in self.contentView.subviews {
            view.removeFromSuperview()
        }
        self.bannerImagesArr.removeAll()

        if let cusViews = self.customViews, cusViews.count > 0{//  如果设置了自定义view 绘制自定义界面不做图片绘制
            var newCusViews: [UIView] = []
            newCusViews.append(contentsOf: cusViews)
//            if self.isCycleRunBanner, let firstView = cusViews.first, let lastView = cusViews.last {
//                newCusViews.insert(lastView, at: 0)
//                newCusViews.append(firstView)
//            }
            for index in 0 ..< (newCusViews.count) {
                guard let view  = newCusViews.objAt(index: index) else { continue }
                self.bannerImagesArr.append(view)
                self.contentView.addSubview(view)
            }
            self.updateSubImagesFrame()
            return
        }
        if self.bannerArr?.count == 0 || self.bannerArr == nil {
            return
        }
        let willWriteBannerArr = NSMutableArray.init(array: self.bannerArr ?? [])
        if self.isCycleRunBanner {
            willWriteBannerArr.insert(self.bannerArr?.last as Any, at: 0)
            willWriteBannerArr.add(self.bannerArr?.first as Any)
        }
        var index = 0
        while index < willWriteBannerArr.count {
            let obj = willWriteBannerArr.object(at: index)
            let imageview = UIImageView.init()
            imageview.object = obj
            //set tag
            if self.isCycleRunBanner{
                if index == 0{
                    imageview.tag = (self.bannerArr?.count)! - 1
                }else if index == willWriteBannerArr.count - 1{
                    imageview.tag = 0
                }else{
                    imageview.tag = index - 1
                }
            }else{
                imageview.tag = index
            }
            //setimage
            if obj is String{
                imageview.image = UIImage.init(named: obj as! String)
            }
            self.delegate?.createBannerImages?(imageView: imageview, object: obj)
            self.createBannerImagesBlock?(imageview.tag, imageview, obj)
//            if (self.delegate != nil && self.delegate?.responds(to: #selector(BannerDelegate.createBannerImages(imageView:object:))) ?? false) == false && (self.createBannerImagesBlock != nil) == false {
//                imageview.image = UIImage.init(named: obj as! String)
//            }else{
//                self.createBannerImagesBlock?(imageview,obj)
//                if (self.delegate != nil && self.delegate?.responds(to: #selector(BannerDelegate.createBannerImages(imageView:object:))) ?? false){
//                    self.delegate?.createBannerImages!(imageView: imageview, object: obj)
//                }
//            }
            
            //add tapges
            if self.isAddGestureWhenCreate?(imageview.tag, imageview, obj) ?? true{
                let tap = UITapGestureRecognizer.init(target: self, action: #selector(imageClicked(tap:)))
                imageview.addGestureRecognizer(tap)
            }
            imageview.isUserInteractionEnabled = true
            
            //create subviews and images
            self.bannerImagesArr.append(imageview)
            self.contentView.addSubview(imageview)
            index = index + 1
        }
        updateSubImagesFrame()
    }

    @objc public func runTo(_ page:Int,animation:Bool) -> Void {
        var pageCount: Int = page
        if self.isCycleRunBanner && self.customViews == nil {
            pageCount += 1
        }
        if let view = self.bannerImagesArr.objAt(index: pageCount) {
            self.pageControl.currentPage = page
            self.setContentView(offset: CGPoint(x: (view.x - inset.left) - self.itemSpace / 2.0, y: 0), animation: animation, isRunBlock: false)
        }
    }

    @objc public func imageClicked(tap:UITapGestureRecognizer){
        let imageview = tap.view
        if imageview?.object == nil && imageview != nil{
            return
        }
        self.bannerClickedBlock?((imageview?.object)!,self,(imageview?.tag)!)
        if (self.delegate != nil && self.delegate?.responds(to: #selector(BannerDelegate.bannerClicked(object:banner:index:))) ?? false) {
            self.delegate?.bannerClicked!(object: (imageview?.object)!, banner: self, index: (imageview?.tag)!)
        }
    }

    /// 刷新frame
    private func updateSubImagesFrame() -> Void {
        let frame = CGRect(x: self.contentInset.left - self.itemSpace / 2,
                           y: self.contentInset.top,
                           width: self.width - self.contentInset.left - self.contentInset.right + self.itemSpace,
                           height: self.height - self.contentInset.top - self.contentInset.bottom)
        self.contentView.frame = frame
        
        self.pageControl.width = self.width
        self.pageControl.x = self.pageOffset.x
        self.pageControl.y = self.height - self.pageControl.height - self.inset.bottom + self.pageOffset.y
        self.bringSubviewToFront(self.pageControl)
//        changePageControlAlignment()
        
        var view_x : CGFloat = self.itemSpace / 2, view_y : CGFloat = 0, view_width : CGFloat = self.contentView.width - self.itemSpace,view_height : CGFloat = self.contentView.height
        
        var index = 0
        while index < self.bannerImagesArr.count {
            guard let imageview = self.bannerImagesArr.objAt(index: index) else { continue }
            var frame = CGRect.init(x: view_x, y: view_y, width: view_width, height: view_height)
//            imageview.frame = CGRect.init(x: view_x, y: view_y, width: view_width, height: view_height)
            frame.x += inset.left
            frame.y += inset.top
            frame.zz_width -= (inset.left + inset.right)
            frame.zz_height -= (inset.top + inset.bottom)
            imageview.frame = frame
            view_x = view_x + view_width + CGFloat(self.itemSpace)
            
            index = index + 1
        }
        self.contentView.contentSize = CGSize.init(width: view_x - self.itemSpace / 2, height: 0)
        if self.bannerImagesArr.count > 0 {
            self.runTo(self.pageControl.currentPage, animation: false)
        }else{
            if self.isCycleRunBanner && self.customViews == nil {
                self.contentView.setContentOffset(CGPoint.init(x: self.contentView.width, y: 0), animated: false)
            }else{
                self.contentView.contentOffset = CGPoint.zero
            }
        }
        self.refreshPage(isRunBlock: false)
        if self.isAutoRun {
            runBanner()
        }
    }

    private func refreshPage(isRunBlock: Bool){
        let scrollView = self.contentView
        let offset = scrollView.contentOffset

        if self.isCycleRunBanner && self.customViews == nil{
            var page : CGFloat = 0

            if (offset.x >= (self.contentView.contentSize.width - self.contentView.width)){
                self.contentView.setContentOffset(CGPoint.init(x: self.contentView.width, y: 0), animated: false)
                page = 0
            }else if(offset.x < 0){
                self.contentView.setContentOffset(CGPoint.init(x: self.contentView.contentSize.width - self.contentView.width * 2, y: 0), animated: false)
                page = CGFloat((self.bannerArr?.count ?? 0) - 1)
            }else{
                page = (offset.x - self.contentView.width) / self.contentView.width;
            }
            if page < 0{
                page = CGFloat((self.bannerArr?.count ?? 0) - 1)
            }else if (Int(page) > (self.bannerArr?.count ?? 0) - 1){
                page = 0
            }
            ZZLog("page is  aaa \(page)")
            self.pageControl.currentPage = lroundf(Float(page))
            if isRunBlock{
                if ZZTool.isIntNumber(string: String(describing: page)){
                    if oldPage == Int(page) {
                        return
                    }
                    oldPage = Int(page)
                    if let imageView = self.bannerImagesArr.objAt(index: Int(page)) {
                        self.changedBlock?(Int(page),imageView, self)
                    }
                    if (delegate != nil && delegate?.responds(to: #selector(BannerDelegate.changed(page:banner:))) ?? false){
                        delegate?.changed!(page: Int(page), banner: self)
                    }
                }
            }
        }else{
            let page = (offset.x) / self.contentView.width;
            if page < 0 || page.isNaN { return }
            self.pageControl.currentPage = lroundf(Float(page))
            if oldPage == Int(page) {
                return
            }
            if isRunBlock{
                self.changedProportionBlock?(page, self)
                if (delegate != nil && delegate?.responds(to: #selector(BannerDelegate.changed(proportion:banner:))) ?? false) {
                    delegate?.changed?(proportion: page, banner: self)
                }
                if let imageView = self.bannerImagesArr.objAt(index: Int(page)){
                    if ZZTool.isIntNumber(string: String(describing: page)){
                        self.changedBlock?(Int(page),imageView,self)
                        if (delegate != nil && delegate?.responds(to: #selector(BannerDelegate.changed(page:banner:))) ?? false) {
                            delegate?.changed!(page: Int(page), banner: self)
                        }
                    }
                }
            }
        }
    }

    open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
        if view == self{
            return self.contentView
        }
        return view
    }

    /// 是否用户正在做一次滑动操作
    private var isUserScrolling: Bool = false
    
    //MARK: - UIScrollViewDelegate
    @objc public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        guard scrollView == self.contentView else { return}
        if self.isAutoRun {
            runBanner()
        }
        self.contentView.isScrollEnabled = true
        self.isUserScrolling = false
    }

    @objc public func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
        self.contentView.isScrollEnabled = false
    }
    
    @objc public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        guard scrollView == self.contentView else { return}
        if self.autoRunTimer != nil {
            self.autoRunTimer?.invalidate()
        }
        self.isUserScrolling = true
    }
    
    @objc public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard scrollView == self.contentView , self.isUserScrolling else { return}
        self.refreshPage(isRunBlock: true)
    }
}
